//
// Created by syshe on 2022/3/22.
//

#ifndef GENERAL_OCR_EXCEPTION_H
#define GENERAL_OCR_EXCEPTION_H

#ifdef __GNUC__
#define CJLU_NOINLINE __attribute__((noinline))
#elif _MSC_VER
#define CJLU_NOINLINE __declspec(noinline)
#else
#define CJLU_NOINLINE
#endif

#if defined(_MSC_VER)
#define CJLU_ALWAYS_INLINE __forceinline
#elif __has_attribute(always_inline) || defined(__GNUC__)
#define CJLU_ALWAYS_INLINE __attribute__((__always_inline__)) inline
#else
#define CJLU_ALWAYS_INLINE inline
#endif

#if defined(_MSC_VER)
#define CJLU_ATTR_VISIBILITY_HIDDEN
#elif defined(__GNUC__)
#define CJLU_ATTR_VISIBILITY_HIDDEN __attribute__((__visibility__("hidden")))
#else
#define CJLU_ATTR_VISIBILITY_HIDDEN
#endif

#define CJLU_ERASE CJLU_ALWAYS_INLINE CJLU_ATTR_VISIBILITY_HIDDEN

#ifdef CJLU_BUILD_MAIN_LIB
#define CJLU_API CJLU_EXPORT
#else
#define CJLU_API CJLU_IMPORT
#endif

// This one is being used by libtorch.so
#ifdef CAFFE2_BUILD_MAIN_LIB
#define TORCH_API CJLU_EXPORT
#else
#define TORCH_API CJLU_IMPORT
#endif

#define CJLU_EXPAND_MSVC_WORKAROUND(x) x
#define CJLU_EXPAND_MSVC_WORKAROUND(x) x

#if defined(__CUDACC__)
#define CJLU_UNLIKELY_OR_CONST(e) e
#else
#define CJLU_UNLIKELY_OR_CONST(e) CJLU_UNLIKELY(e)
#endif
#if defined(__GNUC__) || defined(__ICL) || defined(__clang__)
#define CJLU_LIKELY(expr) (__builtin_expect(static_cast<bool>(expr), 1))
#define CJLU_UNLIKELY(expr) (__builtin_expect(static_cast<bool>(expr), 0))
#else
#define CJLU_LIKELY(expr) (expr)
#define CJLU_UNLIKELY(expr) (expr)
#endif
#if defined(__CUDACC__)
#define CJLU_UNLIKELY_OR_CONST(e) e
#else
#define CJLU_UNLIKELY_OR_CONST(e) CJLU_UNLIKELY(e)
#endif

#define CJLU_THROW_ERROR(err_type, msg) \
  throw ::c10::err_type(               \
      {__func__, __FILE__, static_cast<uint32_t>(__LINE__)}, msg)

void torchCheckFail(
        const char* func,
        const char* file,
        uint32_t line,
        const std::string& msg) {
    throw ::c10::Error({func, file, line}, msg);
}

void torchCheckFail(
        const char* func,
        const char* file,
        uint32_t line,
        const char* msg) {
    throw ::c10::Error({func, file, line}, msg);
}

void torchInternalAssertFail(
        const char* func,
        const char* file,
        uint32_t line,
        const char* condMsg,
        const std::string& userMsg) {
    torchCheckFail(func, file, line, c10::str(condMsg, userMsg));
}

inline CJLU_API const char* torchCheckMsgImpl(const char* msg) {
    return msg;
}
// If there is just 1 user-provided C-string argument, use it.
inline CJLU_API const char* torchCheckMsgImpl(
        const char* /*msg*/,
        const char* args) {
    return args;
}

#define TORCH_INTERNAL_ASSERT(cond, ...)                                         \
  if (CJLU_UNLIKELY_OR_CONST(!(cond))) {                                          \
      torchInternalAssertFail(                                                    \
        __func__,                                                                \
        __FILE__,                                                                \
        static_cast<uint32_t>(__LINE__),                                         \
        #cond                                                                    \
        " INTERNAL ASSERT FAILED at " CJLU_STRINGIZE(__FILE__) ":" CJLU_STRINGIZE( \
            __LINE__) ", please report a bug to PyTorch. ",                      \
        c10::str(__VA_ARGS__));                                                  \
  }
#endif

#define TORCH_CHECK_MSG(cond, type, ...)                   \
  (torchCheckMsgImpl(                                      \
      "Expected " #cond                                    \
      " to be true, but got false.  "                      \
      "(Could this error message be improved?  If so, "    \
      "please report an enhancement request to PyTorch.)", \
      ##__VA_ARGS__))
#define TORCH_CHECK_WITH_MSG(error_t, cond, type, ...)                  \
  if (CJLU_UNLIKELY_OR_CONST(!(cond))) {                                 \
    CJLU_THROW_ERROR(error_t, TORCH_CHECK_MSG(cond, type, __VA_ARGS__)); \
  }
#endif


#ifdef STRIP_ERROR_MESSAGES
#define TORCH_CHECK(cond, ...)                   \
  if (CJLU_UNLIKELY_OR_CONST(!(cond))) {         \
        torchCheckFail(                          \
        __func__,                                \
        __FILE__,                                \
        static_cast<uint32_t>(__LINE__),         \
        TORCH_CHECK_MSG(cond, "", __VA_ARGS__)); \
  }
#else
#define TORCH_CHECK(cond, ...)                     \
  if (CJLU_UNLIKELY_OR_CONST(!(cond))) {            \
        torchCheckFail(                             \
        __func__,                                  \
        __FILE__,                                  \
        static_cast<uint32_t>(__LINE__),           \
        TORCH_CHECK_MSG(cond, "", ##__VA_ARGS__)); \
  }
#endif

// An utility macro that does what `TORCH_CHECK` does if compiled in the host
// code, otherwise does nothing. Supposed to be used in the code shared between
// host and device code as an alternative for `TORCH_CHECK`.
#if defined(__CUDACC__) || defined(__HIPCC__)
#define TORCH_CHECK_IF_NOT_ON_CUDA(cond, ...)
#else
#define TORCH_CHECK_IF_NOT_ON_CUDA(cond, ...) TORCH_CHECK(cond, ##__VA_ARGS__)
#endif

// Debug only version of TORCH_INTERNAL_ASSERT. This macro only checks in debug
// build, and does nothing in release build.  It is appropriate to use
// in situations where you want to add an assert to a hotpath, but it is
// too expensive to run this assert on production builds.
#ifdef NDEBUG
// Optimized version - generates no code.
#define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) \
  while (false)                               \
  CJLU_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(__VA_ARGS__))
#else
#define TORCH_INTERNAL_ASSERT_DEBUG_ONLY(...) \
  CJLU_EXPAND_MSVC_WORKAROUND(TORCH_INTERNAL_ASSERT(__VA_ARGS__))
#endif

// TODO: We're going to get a lot of similar looking string literals
// this way; check if this actually affects binary size.

// Like TORCH_CHECK, but raises LinAlgError instead of Error.
#define TORCH_CHECK_LINALG(cond, ...) \
  TORCH_CHECK_WITH_MSG(LinAlgError, cond, "LINALG", __VA_ARGS__)

// Like TORCH_CHECK, but raises IndexErrors instead of Errors.
#define TORCH_CHECK_INDEX(cond, ...) \
  TORCH_CHECK_WITH_MSG(IndexError, cond, "INDEX", __VA_ARGS__)

// Like TORCH_CHECK, but raises ValueErrors instead of Errors.
#define TORCH_CHECK_VALUE(cond, ...) \
  TORCH_CHECK_WITH_MSG(ValueError, cond, "VALUE", __VA_ARGS__)

// Like TORCH_CHECK, but raises TypeErrors instead of Errors.
#define TORCH_CHECK_TYPE(cond, ...) \
  TORCH_CHECK_WITH_MSG(TypeError, cond, "TYPE", __VA_ARGS__)

// Like TORCH_CHECK, but raises NotImplementedErrors instead of Errors.
#define TORCH_CHECK_NOT_IMPLEMENTED(cond, ...) \
  TORCH_CHECK_WITH_MSG(NotImplementedError, cond, "TYPE", __VA_ARGS__)


// Report an error with a specific argument
// NOTE: using the argument name in TORCH_CHECK's message is preferred
#define TORCH_CHECK_ARG(cond, argN, ...) \
  TORCH_CHECK(cond, "invalid argument ", argN, ": ", __VA_ARGS__)

#endif //GENERAL_OCR_EXCEPTION_H
